package com.sweet.imserver.server;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.sweet.imserver.exception.SwRuntimeException;
import com.sweet.imserver.model.Member;
import com.sweet.imserver.utils.BaseUtils;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Promise;
import io.vertx.core.eventbus.EventBus;
import io.vertx.core.eventbus.ReplyException;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServerOptions;
import io.vertx.core.impl.logging.Logger;
import io.vertx.core.impl.logging.LoggerFactory;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.auth.JWTOptions;
import io.vertx.ext.auth.jwt.JWTAuth;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.ext.web.handler.CorsHandler;
import io.vertx.ext.web.handler.HttpException;
import io.vertx.ext.web.handler.JWTAuthHandler;

import java.util.Arrays;

import static com.sweet.imserver.dao.DaoVerticle.*;

public class HttpServerVerticle extends AbstractVerticle {
  private static final Logger logger = LoggerFactory.getLogger(HttpServerVerticle.class);

  private JwtAuthService jwtAuthService;

  @Override
  public void start(Promise<Void> promise) throws Exception {
    Router router = Router.router(vertx);
    EventBus eventBus = vertx.eventBus();
    jwtAuthService = new JwtAuthService(vertx);

    router.route().handler(BodyHandler.create()).handler(CorsHandler.create("*")
      .allowedMethods(CollUtil.newHashSet(HttpMethod.POST, HttpMethod.GET)));

    router.route().failureHandler(this::failureHandler);
    router.route("/api/private/*").handler(JWTAuthHandler.create(jwtAuthService.authProvider));

    // 注册
    router.post("/api/register").handler(ctx -> registerHandler(eventBus, jwtAuthService.authProvider, ctx));
    // 登录
    router.post("/api/login").handler(ctx -> loginHandler(eventBus, jwtAuthService.authProvider, ctx));
    // 查询好友
    router.get("/api/private/friends").handler(ctx -> queryFriendsHandler(eventBus, ctx));
    // 根据手机号搜索
    router.post("/api/private/search").handler(ctx -> searchHandler(eventBus, ctx));
    // 发起申请好友
    router.post("/api/private/friends/apply").handler(ctx -> applyFriendsHandler(eventBus, ctx));
    // 处理好友申请
    router.post("/api/private/friends/apply/status").handler(ctx -> applyFriendsStatusHandler(eventBus, ctx));
    // 查询好友申请记录
    router.get("/api/private/friends/apply").handler(ctx -> queryFriendsApplyHandler(eventBus, ctx));
    // 查询最近聊天列表
    router.get("/api/private/friends/chat").handler(ctx -> queryFriendsChatListHandler(eventBus, ctx));
    // 查询聊天消息
    router.get("/api/private/friends/message").handler(ctx -> queryFriendsMessageListHandler(eventBus, ctx));

    vertx.createHttpServer(new HttpServerOptions().setTcpKeepAlive(true))
      .requestHandler(router)
      .listen(9099, res -> {
        if (res.succeeded()) {
          logger.debug("http server start success 9099");
          promise.complete();
        } else {
          logger.error("http server fail", res.cause());
          promise.fail(res.cause());
        }
      });
  }

  private void queryFriendsMessageListHandler(EventBus eventBus, RoutingContext ctx) {
    String mid = ctx.user().principal().getString("m");
    String fid = ctx.queryParam("fid").get(0);
    String start = ctx.queryParam("start").get(0);
    Integer length = Integer.parseInt(ctx.queryParam("length").get(0));
    eventBus.<JsonArray>request(FRIEND_CHAT_MESSAGE_LIST_ADDRESS, new JsonObject().put("mid", mid)
      .put("fid", fid).put("start", Integer.parseInt(start))
      .put("length", length > 30 ? 30 : length), resp -> {
      if (resp.succeeded()) {
        successResp(ctx, resp.result().body());
      } else {
        errorResp(ctx, resp.cause());
      }
    });
  }

  private void queryFriendsChatListHandler(EventBus eventBus, RoutingContext ctx) {
    String mid = ctx.user().principal().getString("m");
    eventBus.<JsonArray>request(FRIEND_CHAT_LIST_ADDRESS, mid, resp -> {
      if (resp.succeeded()) {
        successResp(ctx, resp.result().body());
      } else {
        errorResp(ctx, resp.cause());
      }
    });
  }

  private void queryFriendsHandler(EventBus eventBus, RoutingContext ctx) {
    String mid = ctx.user().principal().getString("m");
    eventBus.<JsonArray>request(FRIENDS_ADDRESS, mid, resp -> {
      if (resp.succeeded()) {
        successResp(ctx, resp.result().body());
      } else {
        errorResp(ctx, resp.cause());
      }
    });
  }

  private void applyFriendsStatusHandler(EventBus eventBus, RoutingContext ctx) {
    JsonObject bodyAsJson = ctx.getBodyAsJson();
    String mid = ctx.user().principal().getString("m");
    BaseUtils.checkNotBank(bodyAsJson.getString("id"), "缺少参数");
    Integer agree = bodyAsJson.getInteger("agree");
    BaseUtils.checkNotBank(agree, "缺少参数");
    if (agree < 0 || agree > 2) {
      throw new SwRuntimeException("参数不合法");
    }
    eventBus.<Void>request(ADD_FRIEND_STATUS_ADDRESS,
      new JsonObject().put("id", bodyAsJson.getString("id"))
        .put("mid", mid).put("agree", agree), resp -> {
        if (resp.succeeded()) {
          successResp(ctx, new JsonObject());
        } else {
          errorResp(ctx, resp.cause());
        }
      });
  }

  private void queryFriendsApplyHandler(EventBus eventBus, RoutingContext ctx) {
    eventBus.<JsonArray>request(QUERY_FRIEND_APPLY_ADDRESS,
      new JsonObject().put("id", ctx.user().principal().getString("m")), resp -> {
        if (resp.succeeded()) {
          successResp(ctx, resp.result().body());
        } else {
          errorResp(ctx, resp.cause());
        }
      });
  }

  private void applyFriendsHandler(EventBus eventBus, RoutingContext ctx) {
    String mid = ctx.user().principal().getString("m");
    JsonObject bodyAsJson = ctx.getBodyAsJson();
    String fid = bodyAsJson.getString("fid");
    String note = bodyAsJson.getString("note");

    eventBus.<Boolean>request(ADD_FRIEND_ADDRESS, new JsonObject()
        .put("id", mid).put("fid", fid).put("note", note),
      resp -> {
        if (resp.succeeded()) {
          successResp(ctx, new JsonObject());
        } else {
          errorResp(ctx, resp.cause());
        }
      });
  }

  private void searchHandler(EventBus eventBus, RoutingContext ctx) {
    String phone = ctx.getBodyAsJson().getString("phone");
    String mid = ctx.user().principal().getString("m");
    BaseUtils.checkNotBank(phone, "手机号不能为空");
    eventBus.<JsonObject>request(SEARCH_ADDRESS, new JsonObject()
      .put("phone", phone)
      .put("mid", Long.parseLong(mid)), resp -> {
      if (resp.succeeded()) {
        successResp(ctx, resp.result().body());
      } else {
        errorResp(ctx, resp.cause());
      }
    });
  }

  private void registerHandler(EventBus eventBus, JWTAuth authProvider, RoutingContext ctx) {
    JsonObject bodyAsJson = ctx.getBodyAsJson();
    if (bodyAsJson.containsKey("password")) {
      bodyAsJson.put("password", DigestUtil.sha256Hex(bodyAsJson.getString("password")));
    }
    Member member = Member.fromJson(bodyAsJson).checkRegisterMember();
    eventBus.<String>request(REGISTER_ADDRESS, member.toJson(), resp -> {
      if (resp.succeeded()) {
        String memberId = resp.result().body();
        String token = authProvider.generateToken(new JsonObject()
            .put("m", memberId)
            .put("t", System.currentTimeMillis()),
          new JWTOptions().setExpiresInMinutes(60 * 2));
        bodyAsJson.remove("password");
        successResp(ctx, new JsonObject().put("token", token).put("m", bodyAsJson));
      } else {
        errorResp(ctx, resp.cause());
      }
    });
  }

  private void loginHandler(EventBus eventBus, JWTAuth authProvider, RoutingContext ctx) {
    JsonObject bodyAsJson = ctx.getBodyAsJson();
    String phone = bodyAsJson.getString("phone");
    String password = bodyAsJson.getString("password");
    BaseUtils.checkNotBank(phone, "请填写手机号");
    BaseUtils.checkNotBank(password, "请填写密码");
    eventBus.<JsonObject>request(LOGIN_ADDRESS, bodyAsJson, resp -> {
      if (resp.succeeded()) {
        JsonObject member = resp.result().body();
        String token = authProvider.generateToken(new JsonObject()
            .put("m", member.getString("id"))
            .put("t", System.currentTimeMillis()),
          new JWTOptions().setExpiresInMinutes(60 * 24 * 7));
        member.remove("password");
        member.put("id", member.getString("id"));
        successResp(ctx, new JsonObject().put("token", token).put("m", member));
      } else {
        errorResp(ctx, resp.cause());
      }
    });
  }


  private void successResp(RoutingContext ctx, JsonObject data) {
    JsonObject resp = new JsonObject()
      .put("code", "200")
      .put("msg", "ok");
    if (data == null) {
      resp.putNull("data");
    } else {
      resp.put("data", data);
    }
    ctx.response()
      .putHeader("Content-Type", "application/json")
      .end(resp
        .toBuffer());
  }

  private void successResp(RoutingContext ctx, JsonArray list) {
    JsonObject resp = new JsonObject()
      .put("code", "200")
      .put("msg", "ok")
      .put("data", new JsonObject().put("list", list));

    ctx.response()
      .putHeader("Content-Type", "application/json")
      .end(resp
        .toBuffer());
  }

  private void successResp(RoutingContext ctx) {
    ctx.response()
      .putHeader("Content-Type", "application/json")
      .end(new JsonObject()
        .put("code", "200")
        .put("msg", "ok")
        .toBuffer());
  }

  private void errorResp(RoutingContext ctx, Throwable cause) {
    JsonObject resp = new JsonObject();
    String err;
    String errCode = "-1";
    if (cause instanceof ReplyException) {
      ReplyException replyException = (ReplyException) cause;
      err = replyException.getMessage();
      errCode = replyException.failureCode() + "";
    } else {
      err = cause.getMessage();
    }
    resp.put("code", errCode);
    resp.put("msg", err == null ? "未知错误" : err);

    ctx.response()
      .putHeader("Content-Type", "application/json")
      .end(resp.toBuffer());
  }

  private void failureHandler(RoutingContext ctx) {
    Throwable failure = ctx.failure();
    logger.error("统一处理异常Class:" + failure);
    String err;
    String code = "-1";
    if (failure instanceof SwRuntimeException) {
      err = failure.getMessage();
    } else if (failure instanceof HttpException) {
      err = failure.getMessage();
      if ("Unauthorized".equals(err)) {
        code = "-2";
        err = "登录失效，请重新登录";
      }
    } else {
      err = "未知错误";
      logger.error("统一处理异常", failure);
    }
    JsonObject obj = new JsonObject();
    obj.put("code", code);
    obj.put("msg", err);
    ctx.response().putHeader("content-type", "application/json")
      .end(obj.toBuffer());
  }
}
